﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
namespace oreimou
{
    //文件节点基类
    abstract class Pnode
    {

        protected FileContent fc;
        long oft;
        long size;
        string name;
        protected FileContent FileC{get{return fc;}}
        public long Offset { get { return oft; } }
        public long Length { get { return size; } }
        public string FileName { get { return name; } }
        public Pnode(long o, int s, string n,FileContent fc)
        {
            oft = o;
            size = s;
            name = n;
            this.fc = fc;
        }

        public int GetInt32(long offset=-1)//读取32长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            return (int)fc.ReadInteger(4);
        }
        public long GetInt64(long offset=-1)        //读取64长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            return fc.ReadInteger(8);
        }
        public int GetInt16(long offset=-1)        //读取16长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            return (int)fc.ReadInteger(2);
        }

        public string GetStringANSI(int len,long offset=-1)            //读取ansi字符串
        {
            if (offset>=0) fc.Seek(offset + oft);
            return fc.ReadString(len, Encoding.ASCII);
        }
        public string GetStringUTF8(int len, long offset=-1)
        {
            if (offset>=0) fc.Seek(offset + oft);
            return fc.ReadString(len, Encoding.UTF8);
        }

        public void Write(int number,long offset=-1)//写32长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            fc.Write(number);
        }
        public void Write(long number,long offset=-1)//写64长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            fc.Write(number);
        }
        public void Write(short number,long offset=-1)//写16长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            fc.Write(number);
        }
        public void Write(string str,long offset=-1)//写16长度整数
        {
            if (offset>=0) fc.Seek(offset + oft);
            fc.Write(str);
        }
        protected void Seek(long offset)
        {
            fc.Seek(offset+oft);
        }
        protected void SetSize(long size)
        {
            this.size=size;
        }
        protected byte[] GetHead(long offset=0)
        {
            return fc.FileHead(offset+oft);
        }
        protected void Output(string path,string spname="",FileContent.CpAlgo algo=null)
        {
            if (algo == null)
            {
                if (spname != "")
                {
                    fc.Output(oft, size, path, spname);
                }
                else
                    fc.Output(oft, size, path, name);
            }
            else
            {
                if (spname != "")
                {
                    fc.OutputCompress(oft, size, path, spname,algo);
                }
                else
                    fc.OutputCompress(oft, size, path, name,algo);
            }
        }

        protected void OutputFolder(string path,int offset,int size,int cnt)
        {
            fc.OutputFolder(path,name,offset,size,cnt);
        }
        public abstract void Exract(string path);

    };
    class Pother:Pnode
    {
        public Pother(FileContent fc,long o, int s, string n)
            :base(o,s,n,fc)
        {
        }
        public override void Exract(string path)
        {
 	         Output(path);
        }
    }
    //gpda类
    class Pgpda : Pnode
    {

        private static string CHANGE = @"E:\game\test\oreimou\obj_zip";
        int Count { get { return cnt; } }
        int cnt;
        ArrayList items;


        public Pgpda(FileContent fc,long o, int s, string n)
            : base(o, s, n, fc)
        {
            items = new ArrayList();

            //如果文件长度在子文件中定义，此时再读取
            if (!fc.ForWrite)
            {
                if (Length < 0)
                    base.SetSize(GetInt64(0x4));

                cnt = GetInt32(0xC);
            }
        }
        public void GetItems()//读取子文件
        {
            int i;
            long pos = 0;
            for (i = 0; i < cnt; i++)
            {
                pos += 0x10;
                Seek(pos);
                long OffsetC = GetInt64();
                int SizeC = GetInt32();
                int StringOffset = GetInt32();
                int StringSize = GetInt32(StringOffset);
                string FileName = GetStringANSI(StringSize);

                byte[] head = GetHead(OffsetC);

                //文件类型判断
                if (TypeCheck.GPDAcheck(head))
                {
                    Pgpda child = new Pgpda(FileC, Offset+OffsetC, SizeC, base.FileName+'\\'+FileName);
                    items.Add(child);
                    child.GetItems();
                }
                else if (TypeCheck.GZipcheck(head))
                {
                    items.Add(new Pgzip(FileC, Offset + OffsetC, SizeC, base.FileName + '\\' + FileName));
                }
                else
                {
                    items.Add(new Pother(FileC, Offset + OffsetC, SizeC, base.FileName + '\\' + FileName));
                }
            }
        }

        public long GetItemsDir(DirectoryInfo dir)//从文件夹读取子文件用于打包
        {
            long cur_size=0;
            int count = 0;
            int head_size = GetHeadSize(dir);
            cur_size += fc.CreatHead(head_size);
            DirectoryInfo[] dirs = dir.GetDirectories();
            FileInfo[] files = dir.GetFiles();

            Array.Sort(dirs, new SortFile(SortOption.FileName));
            Array.Sort(files, new SortFile(SortOption.FileName));
            FileSystemInfo[] entries = dir.GetFileSystemInfos();
            Array.Sort(entries, new SortFile(SortOption.FileName));
            count = entries.Length;
            Write(count, 0xC);//写文件个数
            foreach (FileSystemInfo entry in entries)
            {
                int size = 0;
                long offset = fc.Length;
                if (entry is FileInfo)
                {
                    FileInfo fi = (FileInfo)entry;
                    if (fi.Name.Contains("obj.gz"))//检查是否为需替换文件
                    {
                        DirectoryInfo cd = new DirectoryInfo(CHANGE);
                        FileInfo[] cfs = cd.GetFiles(fi.Name);
                        if (cfs.Length > 0)
                        {
                            fi = cfs[0];
                            Console.WriteLine(cfs[0].FullName);
                        }
                    }
                    size = (int)(fi.Length);
                    Pother other = new Pother(
                        fc,
                        offset,
                        size,
                        fi.Name
                    );
                    cur_size += fc.Input(fi);
                    items.Add(other);
                }
                else if (entry is DirectoryInfo)
                {
                    Pgpda gpda = new Pgpda(
                        fc,
                        offset,
                        size,
                        entry.Name
                    );
                    size = (int)gpda.GetItemsDir((DirectoryInfo)entry);
                    cur_size += size;
                    gpda.SetSize(size);
                    items.Add(gpda);
                }
            }
            /*
            foreach (DirectoryInfo cur_dir in dirs)//添加包
            {
                int size = 0; ;
                long offset = fc.Length;
                Pgpda gpda = new Pgpda(
                    fc,
                    offset,
                    size,
                    cur_dir.Name
                    );
                size = (int)gpda.GetItemsDir(cur_dir);
                cur_size += size;
                gpda.SetSize(size);
                items.Add(gpda);
            }
            foreach (FileInfo cur_file in files)//添加单独文件
            {
                int size = (int)cur_file.Length;
                long offset = fc.Length;
                Pother other = new Pother(
                    fc,
                    offset,
                    size,
                    cur_file.Name
                    );
                cur_size += fc.Input(cur_file);
                items.Add(other);
            }
            */
            int stroff=items.Count*0x10+0x10;
            int numaddr=0x10;
            Write(cur_size,0x4);

            foreach (Pnode pn in items)
            {
                Write(pn.Offset - this.Offset, numaddr);
                Write(pn.Length, numaddr + 0x8);
                Write(stroff, numaddr + 0xC);
                Write(pn.FileName.Length, stroff);
                stroff += 4;
                Write(pn.FileName, stroff);
                stroff += pn.FileName.Length;
                numaddr += 0x10;
            }

            return cur_size;
        }

        private int GetHeadSize(DirectoryInfo dir)
        {
            int ret=0x10;
            FileSystemInfo[] entries = dir.GetFileSystemInfos();
            ret=ret+entries.Length*0x10;
            foreach (FileSystemInfo entry in entries)
            {
                ret += entry.Name.Length + 4;
            }
            return ret;
        }

        public override void Exract(string path)
        {
            OutputFolder(path,(int)this.Offset,(int)this.Length,this.Count);
            foreach (Pnode pn in items)
            {
                pn.Exract(path);
                
            }
        }
    }
}
